﻿/**
* ...
* @author Default
* @version 0.1
*/
package AS3Events{
	
	public class AS3EventRouter {
	
		/************************************************************
		 * EVENTO DE BROADCAST
		 ***********************************************************/
		private static var _BROADCAST:String = "AS3EventRouter_BROADCAST_CLASS"
		
		/************************************************************
		 * INSTANCIA UNICA
		 ***********************************************************/
		private static var Instance:AS3EventRouter;
		
		/************************************************************
		 * LISTA DAS CLASSES DE EVENTOS
		 ***********************************************************/
		private var eventClassList:Array;
		
		/************************************************************
		 * CLASSE BROADCAST DE EVENTOS
		 ***********************************************************/
		private var broadCast:AS3EventClass;
		
		/************************************************************
		 * CONSTRUTOR PRIVADO PARA O SINGLETON
		 ***********************************************************/
		public function AS3EventRouter() {
			// instancia a lista de classes de eventos
			this.eventClassList = new Array();
			
			// instanciando a lista para broadcast
			this.broadCast = new AS3EventClass(_BROADCAST);
		}
	
		/************************************************************
		 * SINGLETON
		 * @return instancia unica do AS3EventRouter
		 ***********************************************************/
		public static function GetInstance():AS3EventRouter
		{
			if (Instance == null)
			{
				Instance = new AS3EventRouter();
			}
			
			return Instance;
		}
		
		/************************************************************
		 * EVENTO DE BROADCAST
		 ***********************************************************/
		public static function get BROADCAST():String
		{
			return _BROADCAST;
		}
		
		/************************************************************
		 * ENVIA UM EVENTO PARA UMA CLASSE DE EVENTO
		 * @param	eventClassName
		 * @param	message
		 ***********************************************************/
		public static function SendEvent(eventClassName:String, message:AS3EventMessage):void
		{
			// se for um broadcast envia para todo mundo
			if(eventClassName == _BROADCAST) 
			{
				GetInstance().broadCast.sendEvent(message);
			}
			// envia para determinada classe de eventos
			else
			{
				if(GetInstance().getEventClass(eventClassName)!=null)
					GetInstance().getEventClass(eventClassName).sendEvent(message);
			}
			
			// apos enviar um evento remove os listeners que foram removidos
			GetInstance().broadCast.removeNulls();
			RemoveNulls();
		}
		
		/************************************************************
		 * CRIA UMA CLASSE DE EVENTOS
		 * @param	eventClassName nome da classe de eventos
		 * @return instancia da classe de eventos
		 ***********************************************************/
		public static function AddEventClass(eventClassName:String):AS3EventClass
		{	
			// se a classe de eventos ainda nao foi registrada
			if (VerifyEventClassRegistered(eventClassName)==false)
			{
				// instancia uma nova classe de eventos
				var eventClass:AS3EventClass = new AS3EventClass(eventClassName);
				
				// adiciona ao eventRouter a classe de evento
				GetInstance().eventClassList.push(eventClass);
				
				// retorna a referencia para a instancia da classe de evento
				return eventClass;
			}
			
			// caso ja esteja registrada retorna a referencia para a instancia da classe de evento
			return GetInstance().getEventClass(eventClassName);
		}
		
		/************************************************************
		 * CRIA UMA CLASSE DE EVENTOS
		 * @param	eventClassName nome da classe de eventos
		 * @return instancia da classe de eventos
		 ***********************************************************/
		public static function AddEventListener(eventClassName:String, listener:AS3EventRouterListener):void
		{	
			// variavel do tipo classe de eventos
			var eventClass:AS3EventClass;
			
			// referencia para a instancia unica do EventRouter
			var router:AS3EventRouter = GetInstance();
			
			// se a classe de eventos não foi registrada
			if (VerifyEventClassRegistered(eventClassName)==false)
			{
				// instancia uma nova classe de eventos
				eventClass = new AS3EventClass(eventClassName);
				
				// adicionando a nova classe de eventos
				GetInstance().eventClassList.push(eventClass);
			}
			// se a classe de eventos ja existe
			else
			{
				// pega a referencia da classe de eventos
				eventClass = GetInstance().getEventClass(eventClassName);
			}
			
			// se o listener nao foi registrado em nenhuma classe de eventos
			if(router.qtdEventClassToListener(listener)==0)
			{
				// o listener é adicionado na classe de broadcast
				router.broadCast.addListener(listener);
			}
			
			// adicionando o listener na classe de eventos
			eventClass.addListener(listener);
		}
			
		/************************************************************
		 * PEGANDO UMA INSTANCIA DA CLASSE DE UMA DETERMINADA CLASSE DE EVENTOS
		 * @param	eventClassName nome da classe de evento
		 * @return retorna a referencia da instancia da classe de evento
		 ***********************************************************/
		private function getEventClass(eventClassName:String):AS3EventClass
		{
			// referencia para a instancia unica do router
			var router:AS3EventRouter = GetInstance();
			
			// quantidade de classes de eventos registradas
			var length:Number = router.eventClassList.length;
			
			// se for a classe de eventos de broadcast
			if(eventClassName == BROADCAST)
			{
				// retorna a referencia da classe de evento de broadcast
				return this.broadCast;
			}
			
			// varre a lista de classe de eventos para achar a classe de eventos desejada
			for (var i:Number = 0; i < length; i++)
			{
				// referencia para uma determinada classe de evento
				var as3EventClass:AS3EventClass = this.eventClassList[i];
				
				// se for a classe de evento desejada
				if (eventClassName == as3EventClass.getEventClassName())
				{
					// retorna a referencia para a classe de evento
					return as3EventClass;
				}
			}
			
			// caso nao encontre a classe de eventos retorna null
			return null;
		}
		
		/************************************************************
		 * VERIFICA SE A CLASSE DE EVENTOS JA FOI REGISTRADA
		 * @param	eventClass nome da classe de eventos
		 * @return true se a classe de eventos existe e false caso não exista.
		 ***********************************************************/
		public static function VerifyEventClassRegistered(eventClassName:String):Boolean
		{	
			// referencia para a instancia unica do EventRouter
			var router:AS3EventRouter = GetInstance();
			
			// tamanho da lista de classes de eventos
			var length:Number = router.eventClassList.length;
			
			// se for a classe de eventos de broadcast
			if(eventClassName == BROADCAST)
			{
				//retorna true
				return true;
			}
			
			// varre a lista de classe de eventos
			for (var i:Number = 0; i < length; i++)
			{
				// referencia para a classe de evento contida na posicao i
				var as3EventClass:AS3EventClass = router.eventClassList[i];
				
				// se existe
				if (eventClassName == as3EventClass.getEventClassName())
				{
					// retorna true
					return true;
				}
			}
			
			// se nao existe retorna false
			return false;
		}
	
		/************************************************************
		 * REMOVE UMA CLASSE DE EVENTOS E SEUS LISTENERS
		 * @param	eventClass
		 * @return retorna true caso a classe de eventos seja removida e false caso
		 * a classe de eventos nao exista
		 ***********************************************************/
		public static function RemoveEventClass(eventClassName:String):Boolean
		{	
			// referencia para a instancia unica do EventRouter
			var router:AS3EventRouter = GetInstance();
			
			// removendo todos os listeners da classe de evento
			router.getEventClass(eventClassName).removeAllListeners();
			
			// quantidade de classes de eventos no EventRouter
			var length:Number = router.eventClassList.length;
			
			// varre a lista de classes de evento da EventRouter
			for (var i:Number = 0; i < length; i++)
			{
				// referencia para a classe de evento na posicao i
				var as3EventClass:AS3EventClass = router.eventClassList[i];
				
				// se for a classe de eventos que deve ser removida
				if (eventClassName == as3EventClass.getEventClassName())
				{
					// atribui null a posicao i da lista de classe de eventos no EventRouter
					router.eventClassList[i] = null;
					
					// retorna true se o elemento foi removido
					return true;
				}
			}
			
			// retorna false se a classe de eventos não existe
			return false;
		}
		
		/************************************************************
		 * 
		 * @param	listener
		 * @return
		 ***********************************************************/
		private function qtdEventClassToListener(listener:AS3EventRouterListener):Number
		{
			// inicia o contador
			var cont:Number = 0;
			
			// referencia para a instancia unica do EventRouter
			var router:AS3EventRouter = GetInstance();
			
			// quantidade de classes de eventos no EventRouter
			var length:Number = router.eventClassList.length;
			
			// varre a lista de classes de evento
			for(var i:Number = 0; i<length; i++)
			{
				// atribuindo a uma variavel a referencia para a classe de evento na posicao i
				var eventClass:AS3EventClass = this.eventClassList[i];
				
				//se o listener escuta essa classe de evento
				if(eventClass.isListener(listener)==true)
				{
					// incrementa o contador
					cont++;
				}
			}
			
			// retorna o valor final do contador
			return cont;
		}
	
		/************************************************************
		 * REMOVE TODOS OS LISTENERS E INICIA TUDO DE NOVO
		 ***********************************************************/
		public static function ResetEventRouter():void
		{
			// referencia para a instancia unica do EventRouter
			var router:AS3EventRouter = GetInstance(); 
			
			// instanciando novamente a lista de classe de eventos
			router.eventClassList = new Array();
			
			// instanciando novamente a classe de eventos de broadcast
			router.broadCast = new AS3EventClass(AS3EventRouter.BROADCAST);
		}
		
		/************************************************************
		 * REMOVE UM LISTENER DE UMA CLASSE DE EVENTOS
		 * @param	eventClassName
		 * @param	listener
		 ***********************************************************/
		public static function RemoveListener(eventClassName:String, listener:AS3EventRouterListener):void
		{
			// referencia para a instancia unica do EventRouter
			var router:AS3EventRouter = GetInstance();
			
			// se o listener nao esteja registrado nenhuma classe de eventos
			if(router.qtdEventClassToListener(listener)<=1)
			{
				// remove da classe de broadcast
				router.broadCast.removeListener(listener);
			}
			
			// referencia para a classe de eventos cujo listener vai ser removido
			var eventClass:AS3EventClass = GetInstance().getEventClass(eventClassName);
			
			// remove o listener
			eventClass.removeListener(listener);
		}
		
		/************************************************************
		 * REMOVE O LISTENER DE TODAS AS CLASSES DE EVENTOS
		 * @param	listener
		 ***********************************************************/
		public static function RemoveListenerFromAll(listener:AS3EventRouterListener):void
		{
			// referencia para a instancia unica do EventRouter
			var router:AS3EventRouter = GetInstance();
			
			// quantidade de classes de eventos no EventRouter
			var length:Number = router.eventClassList.length;		
			
			// remove o listener da classe de eventos de broadcast
			router.broadCast.removeListener(listener);
				
			// varre a lista de classe de eventos
			for (var i:Number = 0; i < length; i++)
			{			
				// referencia para a classe de eventos na posicao i
				var as3EventClass:AS3EventClass = router.eventClassList[i];
				
				// remove o listener
				as3EventClass.removeListener(listener);
			}
		}
		
		/************************************************************
		 * REMOVE UMA CLASSE DE EVENTOS E SEUS LISTENERS
		 * @param	eventClass
		 * @return retorna true caso a classe de eventos seja removida e false caso
		 * a classe de eventos nao exista
		 ***********************************************************/
		private static function RemoveNulls():void
		{	
			// referencia para a instancia unica do EventRouter
			var router:AS3EventRouter = GetInstance();
			
			// quantidade de classe de eventos no EventRouter
			var length:Number = router.eventClassList.length;
			
			// varre a lista de classe eventos
			for (var i:Number = 0; i < length; i++)
			{
				// referencia para a classe de eventos na posicao i
				var as3EventClass:AS3EventClass = router.eventClassList[i];
				
				// se a classe de eventos foi deletada
				if (as3EventClass == null)
				{
					// remove da lista
					router.eventClassList.splice(i,1);
				}
				// se a classe de eventos nao foi deletada
				else
				{
					// remove todos os listeners com valor null da lista de listeners
					as3EventClass.removeNulls();
				}
			}
		}
	}
}